package com.zzsource.yuapicliensdk.service;


import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.zzsource.yuapicliensdk.client.YuApiClient;
import com.zzsource.yuapicliensdk.exception.ApiException;
import com.zzsource.yuapicliensdk.exception.ErrorCode;
import com.zzsource.yuapicliensdk.exception.ErrorResponse;
import com.zzsource.yuapicliensdk.model.request.BaseRequest;
import com.zzsource.yuapicliensdk.model.response.ResultResponse;
import com.zzsource.yuapicliensdk.utils.SignUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ObjectUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author: QiMu
 * @Date: 2023年09月19日 11:03
 * @Version: 1.0
 * @Description:
 */
@Slf4j
@Data
public abstract class BaseService implements ApiService {
    private YuApiClient yuApiClient;
    /**
     * 网关HOST
     */
    private String gatewayHost = "https://39.108.117.235:8090/api";

    /**
     * 本地测试接口
     * http://localhost:8090/api 是网关地址
     */
    private String testHost = "http://localhost:8090/api";


    /**
     * 检查配置
     *
     * @param yuApiClient qi api客户端
     * @throws ApiException 业务异常
     */
    public void checkConfig(YuApiClient yuApiClient) throws ApiException {
        if (yuApiClient == null && this.getYuApiClient() == null) {
            throw new ApiException(ErrorCode.NO_AUTH_ERROR, "请先配置密钥AccessKey/SecretKey");
        }
        if (yuApiClient != null && !StringUtils.isAnyBlank(yuApiClient.getAccessKey(), yuApiClient.getSecretKey())) {
            this.setYuApiClient(yuApiClient);
        }
    }

    /**
     * 执行请求
     *
     * @param request 请求
     * @return {@link HttpResponse}
     * @throws ApiException 业务异常
     */
    private <O, T extends ResultResponse> HttpResponse doRequest(BaseRequest<O, T> request) throws ApiException {
        try (HttpResponse httpResponse = getHttpRequestByRequestMethod(request).execute()) {
            return httpResponse;
        } catch (Exception e) {
            throw new ApiException(ErrorCode.OPERATION_ERROR, e.getMessage());
        }
    }

    /**
     * 通过请求方法获取http响应
     *
     * @param request 要求
     * @return {@link HttpResponse}
     * @throws ApiException 业务异常
     */
    private <O, T extends ResultResponse> HttpRequest getHttpRequestByRequestMethod(BaseRequest<O, T> request) throws ApiException {
        if (ObjectUtils.isEmpty(request)) {
            throw new ApiException(ErrorCode.OPERATION_ERROR, "请求参数错误");
        }
        String url = request.getPath().trim();
        String method = request.getMethod().trim().toUpperCase();

        if (ObjectUtils.isEmpty(method)) {
            throw new ApiException(ErrorCode.OPERATION_ERROR, "请求方法不存在");
        }
        if (StringUtils.isBlank(url)) {
            throw new ApiException(ErrorCode.OPERATION_ERROR, "请求路径不存在");
        }
        // 添加路径参数
        url = splicingRequest(request, url);
        //log.info("请求方法：{}，请求路径：{}，请求参数：{}", method, url, request.getRequestParams());
        log.info("请求方法：{}，请求路径：{}，请求路径参数：{}，请求头参数：{}，请求体参数：{}",
                method, url, request.getQueryParams(), request.getHeaderParams() ,request.getBodyParams());
        HttpRequest httpRequest;
        switch (method) {
            case "GET": {
                httpRequest = HttpRequest.get(url);
                break;
            }
            case "POST": {
                httpRequest = HttpRequest.post(url);
                break;
            }
            default: {
                throw new ApiException(ErrorCode.OPERATION_ERROR, "不支持该请求");
            }
        }
        return httpRequest
                .addHeaders(getHeaders(yuApiClient))
                .body(JSONUtil.toJsonStr(request.getBodyParams()));
    }

    /**
     * 获取响应数据
     *
     * @param request 要求
     * @return {@link T}
     * @throws ApiException 业务异常
     */
    public <O, T extends ResultResponse> T res(BaseRequest<O, T> request) throws ApiException {
        if (yuApiClient == null || StringUtils.isAnyBlank(yuApiClient.getAccessKey(), yuApiClient.getSecretKey())) {
            throw new ApiException(ErrorCode.NO_AUTH_ERROR, "请先配置密钥AccessKey/SecretKey");
        }
        T rsp;
        try {
            Class<T> clazz = request.getResponseClass();
            rsp = clazz.newInstance();
        } catch (Exception e) {
            throw new ApiException(ErrorCode.OPERATION_ERROR, e.getMessage());
        }
        // 执行请求
        HttpResponse httpResponse = doRequest(request);
        String body = httpResponse.body();
        Map<String, Object> data = new HashMap<>();
        if (httpResponse.getStatus() != 200) {
            ErrorResponse errorResponse = JSONUtil.toBean(body, ErrorResponse.class);
            data.put("errorMessage", errorResponse.getMessage());
            data.put("code", errorResponse.getCode());
        } else {
            try {
                // 尝试解析为JSON对象
                data = new Gson().fromJson(body, new TypeToken<Map<String, Object>>() {
                }.getType());
            } catch (JsonSyntaxException e) {
                // 解析失败，将body作为普通字符串处理
                data.put("value", body);
            }
        }
        rsp.setData(data);
        return rsp;
    }

    /**
     * 拼接Get请求
     *
     * @param request 要求
     * @param url     路径
     * @return {@link String}
     */
    private <O, T extends ResultResponse> String splicingRequest(BaseRequest<O, T> request, String url) {
        StringBuilder urlBuilder = new StringBuilder(url);
        // urlBuilder最后是/结尾，去掉urlBuilder结尾的/
        if (urlBuilder.toString().startsWith("/")) {
            urlBuilder.setLength(urlBuilder.length() - 1);
        }
        if (!request.getQueryParams().isEmpty()) {
            urlBuilder.append("?");
            for (Map.Entry<String, Object> entry : request.getQueryParams().entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue().toString();
                if(StringUtils.isAnyBlank(key, value))continue;
                urlBuilder.append(key).append("=").append(value).append("&");
            }
            urlBuilder.deleteCharAt(urlBuilder.length() - 1);
        }
//        log.info("GET请求路径：{}", urlBuilder);
        return urlBuilder.toString();
    }

    /**
     * 拼接Get请求
     *
     * @param request 要求
     * @param path    路径
     * @return {@link String}
     */
    //private <O, T extends ResultResponse> String splicingGetRequest(BaseRequest<O, T> request, String path) {
    //    StringBuilder urlBuilder = new StringBuilder(testHost);
    //    // urlBuilder最后是/结尾且path以/开头的情况下，去掉urlBuilder结尾的/
    //    if (urlBuilder.toString().endsWith("/") && path.startsWith("/")) {
    //        urlBuilder.setLength(urlBuilder.length() - 1);
    //    }
    //    urlBuilder.append(path);
    //    if (!request.getRequestParams().isEmpty()) {
    //        urlBuilder.append("?");
    //        for (Map.Entry<String, Object> entry : request.getRequestParams().entrySet()) {
    //            String key = entry.getKey();
    //            String value = entry.getValue().toString();
    //            urlBuilder.append(key).append("=").append(value).append("&");
    //        }
    //        urlBuilder.deleteCharAt(urlBuilder.length() - 1);
    //    }
    //    log.info("GET请求路径：{}", urlBuilder);
    //    return urlBuilder.toString();
    //}


    /**
     * 获取请求头
     *
     * @param yuApiClient qi api客户端
     * @return {@link Map}<{@link String}, {@link String}>
     */
    private Map<String, String> getHeaders(YuApiClient yuApiClient) {
        Map<String, String> hashMap = new HashMap<>(4);
        hashMap.put("accessKey", yuApiClient.getAccessKey());
        hashMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        // 随机数
        hashMap.put("nonce", RandomUtil.randomNumbers(6));
        hashMap.put("sign", SignUtils.getSign(hashMap, yuApiClient.getSecretKey()));
        return hashMap;
    }
}
